# launch3.rpy
# 剧情树可视化界面

init python:
    import math
    from collections import deque  # 添加这行导入

    # ==============================
    # 节点类
    # ==============================
    class StoryNode:
        def __init__(self, label_name, x=0, y=0):
            self.label_name = label_name
            self.x = x
            self.y = y
            self.children = []
            self.parents = []
            self.visited = False

        def add_child(self, child_node):
            if child_node not in self.children:
                self.children.append(child_node)
                child_node.parents.append(self)

    # ==============================
    # 可视化控制器
    # ==============================
    class StoryTreeVisualizer:
        def __init__(self):
            self.nodes = {}
            self.start_node = None
            self.camera_x = 0
            self.camera_y = 0
            self.zoom = 1.0
            self.selected_node = None
            self.debug_info = ""

        def build_tree(self, storyline_data):
            """从 JSON 数据构建树"""
            self.nodes = {}
            self.debug_info = f"开始构建树，标签数量: {len(storyline_data.get('labels', {}))}\n"

            for label_name in storyline_data.get("labels", {}).keys():
                self.nodes[label_name] = StoryNode(label_name)
                self.debug_info += f"创建节点: {label_name}\n"

            if "start" in self.nodes:
                self.start_node = self.nodes["start"]
                self.debug_info += f"起始节点: start\n"

            connection_count = 0
            for label_name, label_info in storyline_data.get("labels", {}).items():
                current_node = self.nodes.get(label_name)
                if not current_node:
                    continue

                for jump_target in label_info.get("jumps", []):
                    if jump_target in self.nodes:
                        current_node.add_child(self.nodes[jump_target])
                        connection_count += 1
                        self.debug_info += f"跳转连接: {label_name} → {jump_target}\n"

                for menu in label_info.get("menus", []):
                    for option in menu.get("options", []):
                        jump_target = option.get("jump")
                        if jump_target and jump_target in self.nodes:
                            current_node.add_child(self.nodes[jump_target])
                            connection_count += 1
                            self.debug_info += f"菜单连接: {label_name} → {jump_target}\n"

            self.debug_info += f"总共创建了 {len(self.nodes)} 个节点和 {connection_count} 个连接\n"
            with open("output.txt", "w", encoding="utf-8") as f:
                f.write(self.debug_info)
            self._auto_layout()

        def _auto_layout(self):
            """改进的分层自动布局"""
            if not self.start_node:
                return

            # 使用 BFS 计算层级
            levels = {}
            visited = set()
            queue = deque([(self.start_node, 0)])
            
            while queue:
                node, level = queue.popleft()
                if node.label_name in visited:
                    continue
                visited.add(node.label_name)
                
                levels.setdefault(level, []).append(node)
                for child in node.children:
                    if child.label_name not in visited:
                        queue.append((child, level + 1))

            # 改进的布局计算
            max_nodes_in_level = max(len(nodes) for nodes in levels.values()) if levels else 1
            level_spacing = 200
            node_spacing = 150
            
            for level, nodes in levels.items():
                total_width = (len(nodes) - 1) * node_spacing
                start_x = -total_width / 2
                
                for i, node in enumerate(nodes):
                    node.x = start_x + i * node_spacing
                    node.y = level * level_spacing

            self.debug_info += f"布局完成，{len(levels)} 个层级\n"

        def jump_to_selected(self):
            if self.selected_node:
                return Jump(self.selected_node.label_name)
            return None

    # ==============================
    # 连线显示类
    # ==============================
    class TreeConnections(renpy.Displayable):
        def render(self, width, height, st, at):
            render = renpy.Render(width, height)
            canvas = render.canvas()

            for node in story_tree_visualizer.nodes.values():
                for child in node.children:
                    start_x = 580 + (node.x + story_tree_visualizer.camera_x) * story_tree_visualizer.zoom
                    start_y = 350 + (node.y + story_tree_visualizer.camera_y) * story_tree_visualizer.zoom
                    end_x = 580 + (child.x + story_tree_visualizer.camera_x) * story_tree_visualizer.zoom
                    end_y = 350 + (child.y + story_tree_visualizer.camera_y) * story_tree_visualizer.zoom
                    
                    # 根据连接类型选择颜色
                    line_color = "#ff6b6b"  # 默认红色
                    line_width = 2
                    
                    canvas.line(line_color, (start_x, start_y), (end_x, end_y), line_width)
                    
                    # 添加箭头
                    self.draw_arrow(canvas, (start_x, start_y), (end_x, end_y), line_color)

            return render
        
        def draw_arrow(self, canvas, start, end, color):
            # 简单的箭头绘制逻辑
            import math
            dx = end[0] - start[0]
            dy = end[1] - start[1]
            angle = math.atan2(dy, dx)
            
            arrow_size = 8
            arrow_x = end[0] - arrow_size * math.cos(angle)
            arrow_y = end[1] - arrow_size * math.sin(angle)
            
            # 绘制箭头线段
            canvas.line(color, (arrow_x, arrow_y), (end[0], end[1]), 2)

# ==============================
# 初始化全局可视化器
# ==============================
default story_tree_visualizer = StoryTreeVisualizer()

# ==============================
# 辅助函数
# ==============================
init python:
    def refresh_story_tree():
        import os
        import json
        
        renpy.notify("开始刷新剧情树...")
        
        try:
            # 尝试直接生成数据
            storyline = generate_storyline()
            renpy.notify("✅ 使用 generate_storyline() 成功")
        except Exception as e:
            renpy.notify(f"❌ generate_storyline() 失败: {str(e)}")
            try:
                # 尝试生成 JSON
                generate_storyline_json()
                renpy.notify("✅ generate_storyline_json() 执行完成")
                
                # 尝试读取 JSON 文件
                json_path = os.path.join(renpy.config.gamedir, "storyline.json")
                if os.path.exists(json_path):
                    with open(json_path, "r", encoding="utf-8") as f:
                        storyline = json.load(f)
                    renpy.notify(f"✅ 从 {json_path} 读取成功，找到 {len(storyline.get('labels', {}))} 个标签")
                else:
                    renpy.notify("❌ storyline.json 文件不存在")
                    storyline = {"labels": {}}
                    
            except Exception as e2:
                renpy.notify(f"❌ JSON 方法也失败: {str(e2)}")
                storyline = {"labels": {}}
        
        # 显示最终的标签数量
        label_count = len(storyline.get('labels', {}))
        renpy.notify(f"最终标签数量: {label_count}")
        
        # 构建树 - 添加详细错误处理
        # 构建树后添加调试信息
        try:
            # 尝试直接生成数据
            storyline = generate_storyline()
            renpy.log("✅ 使用 generate_storyline() 成功")
        except Exception as e:
            renpy.log(f"❌ generate_storyline() 失败: {str(e)}")
            try:
                # 尝试生成 JSON
                generate_storyline_json()
                renpy.log("✅ generate_storyline_json() 执行完成")
                
                # 尝试读取 JSON 文件
                json_path = os.path.join(renpy.config.gamedir, "storyline.json")
                if os.path.exists(json_path):
                    with open(json_path, "r", encoding="utf-8") as f:
                        storyline = json.load(f)
                    renpy.log(f"✅ 从 {json_path} 读取成功，找到 {len(storyline.get('labels', {}))} 个标签")
                else:
                    renpy.log("❌ storyline.json 文件不存在")
                    storyline = {"labels": {}}
                    
            except Exception as e2:
                renpy.log(f"❌ JSON 方法也失败: {str(e2)}")
                storyline = {"labels": {}}
        
        # 显示最终的标签数量
        label_count = len(storyline.get('labels', {}))
        renpy.log(f"最终标签数量: {label_count}")
        
        # 构建树 - 添加详细错误处理
        try:
            if hasattr(story_tree_visualizer, 'build_tree'):
                renpy.log("✅ 找到 build_tree 方法")
                story_tree_visualizer.build_tree(storyline)
                
                # 检查是否生成了 debug_info
                if hasattr(story_tree_visualizer, 'debug_info'):
                    debug_length = len(story_tree_visualizer.debug_info)
                    renpy.log(f"✅ 生成调试信息: {debug_length} 字符")
                    
                    # 尝试保存文件
                    try:
                        output_path = os.path.join(renpy.config.gamedir, "output.txt")
                        with open(output_path, "w", encoding="utf-8") as f:
                            f.write(story_tree_visualizer.debug_info)
                        renpy.log(f"✅ 文件已保存: {output_path}")
                    except Exception as save_error:
                        renpy.log(f"❌ 保存文件失败: {str(save_error)}")
                else:
                    renpy.log("❌ 没有生成 debug_info 属性")
            else:
                renpy.log("❌ story_tree_visualizer 没有 build_tree 方法")
                
        except Exception as build_error:
            renpy.log(f"❌ 构建树过程中出错: {str(build_error)}")
        
        renpy.log("剧情树刷新流程完成")
    
    renpy.notify("剧情树刷新流程完成")
    def export_tree_image():
        renpy.notify("导出功能开发中...")

    def show_story_tree():
        refresh_story_tree()
        renpy.show_screen("story_tree_screen")

    def toggle_story_tree():
        if renpy.get_screen("story_tree_screen"):
            renpy.hide_screen("story_tree_screen")
        else:
            show_story_tree()

    def handle_click(node):
        story_tree_visualizer.selected_node = node
        renpy.notify(f"已选中节点: {node.label_name}")

# ==============================
# 剧情树界面
# ==============================
screen story_tree_screen():
    default show_debug = True
    # 添加拖动和缩放处理
    key "mousedown_1" action Function(handle_drag_start)
    key "mouseup_1" action Function(handle_drag_end)
    key "mouse_move" action Function(handle_drag)
    key "mouse_wheel_up" action Function(handle_zoom, 1.1)
    key "mouse_wheel_down" action Function(handle_zoom, 0.9)

    modal True
    zorder 200

    add Solid("#00000080")

    frame:
        xalign 0.5
        yalign 0.5
        xmaximum 1200
        ymaximum 800
        background Frame("gui/frame.png", 12, 12)

        vbox:
            hbox:
                xfill True
                label "🌳 剧情树可视化"
                textbutton "关闭" action Hide("story_tree_screen") xalign 1.0

            fixed:
                xysize (1160, 700)
                # 先绘制连线
                add TreeConnections()
                
                # 然后绘制节点（确保节点在连线上方）
                for node in story_tree_visualizer.nodes.values():
                    # 修正位置计算：使用固定的中心点作为参考
                    $ node_x = 580 + node.x * story_tree_visualizer.zoom + story_tree_visualizer.camera_x
                    $ node_y = 350 + node.y * story_tree_visualizer.zoom + story_tree_visualizer.camera_y
                    $ is_selected = (node == story_tree_visualizer.selected_node)
                    
                    # 调试输出节点位置
                    # python: renpy.notify(f"节点 {node.label_name}: x={node_x}, y={node_y}")
                    
                    # 节点圆形背景 - 使用圆角矩形确保可见
                    add Solid(is_selected and "#ff6b6b" or "#4ecdc4"):
                        pos (int(node_x - 25), int(node_y - 25))
                        xysize (50, 50)
                        radius 25

                    # 节点文本 - 确保在背景上方
                    text node.label_name:
                        xpos int(node_x)
                        ypos int(node_y)
                        anchor (0.5, 0.5)
                        color "#ffffff"  # 改为白色文本
                        size 14
                        bold True
                        outlines [ (2, "#000000", 0, 0) ]  # 黑色描边提高可读性

                    # 可点击区域
                    button:
                        pos (int(node_x - 25), int(node_y - 25))
                        xysize (50, 50)
                        background None
                        action Function(handle_click, node)
                        hovered [SetVariable("hovered_node", node.label_name)]
                        unhovered [SetVariable("hovered_node", "")]

            # 如果没有节点数据，显示提示
            if not story_tree_visualizer.nodes:
                text "没有找到节点数据！":
                    xalign 0.5
                    yalign 0.5
                    color "#ff0000"
                    size 24
                    bold True

            hbox:
                xalign 0.5
                spacing 20
                textbutton "重置视图" action Function(story_tree_visualizer._auto_layout)
                textbutton "跳转到选中" action story_tree_visualizer.jump_to_selected()
                textbutton "刷新数据" action Function(refresh_story_tree)
                textbutton "导出图片" action Function(export_tree_image)
                textbutton "调试信息" action ToggleScreenVariable("show_debug")

            if show_debug:
                frame:
                    xalign 0.5
                    ypos 650
                    xmaximum 1100
                    background "#91e39acc"
                    viewport:
                        scrollbars "vertical"
                        mousewheel True
                        draggable True
                        vbox:
                            if hasattr(story_tree_visualizer, 'debug_info') and story_tree_visualizer.debug_info:
                                text story_tree_visualizer.debug_info:
                                    color "#ffffff"
                                    size 12
                            else:
                                text "暂无调试信息":
                                    color "#ff9999"
init python:
    def handle_drag_start():
        store.drag_start = renpy.get_mouse_pos()
    
    def handle_drag():
        if hasattr(store, 'drag_start'):
            current_pos = renpy.get_mouse_pos()
            dx = current_pos[0] - store.drag_start[0]
            dy = current_pos[1] - store.drag_start[1]
            story_tree_visualizer.camera_x += dx / story_tree_visualizer.zoom
            story_tree_visualizer.camera_y += dy / story_tree_visualizer.zoom
            store.drag_start = current_pos
    
    def handle_zoom(factor):
        story_tree_visualizer.zoom *= factor
        story_tree_visualizer.zoom = max(0.1, min(5.0, story_tree_visualizer.zoom))
# ==============================init python:
    # 添加拖拽相关的全局变量
    drag_start = None
    is_dragging = False

    def handle_drag_start():
        global drag_start, is_dragging
        drag_start = renpy.get_mouse_pos()
        is_dragging = True
    
    def handle_drag_end():
        global drag_start, is_dragging
        drag_start = None
        is_dragging = False
    
    def handle_drag():
        global drag_start
        if drag_start and is_dragging:
            current_pos = renpy.get_mouse_pos()
            dx = (current_pos[0] - drag_start[0]) / story_tree_visualizer.zoom
            dy = (current_pos[1] - drag_start[1]) / story_tree_visualizer.zoom
            story_tree_visualizer.camera_x += dx
            story_tree_visualizer.camera_y += dy
            drag_start = current_pos
    
    def handle_zoom(factor):
        story_tree_visualizer.zoom *= factor
        story_tree_visualizer.zoom = max(0.1, min(5.0, story_tree_visualizer.zoom))
# 屏幕变量

# ==============================
# 快捷键绑定 (F10)
# ==============================
init python:
    config.keymap['story_tree'] = [ 'K_F10' ]
    config.underlay.append(renpy.Keymap(story_tree=toggle_story_tree))